#include "../src/transaction/transaction_manager.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <inttypes.h>

// Test helper functions
static void print_test_header(const char* test_name);
static void print_test_result(const char* test_name, bool passed);

static void print_test_header(const char* test_name) {
    printf("\n=== %s ===\n", test_name);
}

static void print_test_result(const char* test_name, bool passed) {
    printf("%s: %s\n", test_name, passed ? "PASSED" : "FAILED");
}

// Test transaction manager creation and destruction
static bool test_transaction_manager_creation(void) {
    print_test_header("Testing Transaction Manager Creation");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    printf("Created transaction manager successfully\n");
    printf("  Next XID: %" PRIu64 "\n", tm->next_xid);
    printf("  Default isolation: %s\n", isolation_level_to_string(tm->default_isolation));
    printf("  Max active transactions: %u\n", tm->max_active_txns);
    printf("  Lock timeout: %u ms\n", tm->lock_timeout_ms);
    printf("  Deadlock detection: %s\n", tm->enable_deadlock_detection ? "enabled" : "disabled");
    
    txn_manager_destroy(tm);
    printf("Destroyed transaction manager successfully\n");
    
    return true;
}

// Test basic transaction lifecycle
static bool test_transaction_lifecycle(void) {
    print_test_header("Testing Transaction Lifecycle");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    bool all_passed = true;
    
    // Test transaction begin
    transaction_t* txn1 = txn_begin(tm, ISOLATION_READ_COMMITTED);
    if (!txn1) {
        printf("Failed to begin transaction\n");
        all_passed = false;
    } else {
        printf("Transaction %" PRIu64 " started successfully\n", txn1->xid);
        printf("  State: %s\n", txn_state_to_string(txn1->state));
        printf("  Isolation: %s\n", isolation_level_to_string(txn1->isolation_level));
        printf("  Start time: %" PRIu64 "\n", txn1->start_time);
        
        // Verify transaction is active
        if (!txn_is_active(tm, txn1->xid)) {
            printf("Transaction not found in active list\n");
            all_passed = false;
        } else {
            printf("Transaction correctly registered as active\n");
        }
        
        // Test transaction commit
        if (txn_commit(tm, txn1) != 0) {
            printf("Failed to commit transaction\n");
            all_passed = false;
        } else {
            printf("Transaction committed successfully\n");
            
            // Verify transaction is no longer active
            if (txn_is_active(tm, txn1->xid)) {
                printf("Transaction still active after commit\n");
                all_passed = false;
            } else {
                printf("Transaction correctly removed from active list\n");
            }
            
            // Verify transaction is committed
            if (!txn_is_committed(tm, txn1->xid)) {
                printf("Transaction not found in committed list\n");
                all_passed = false;
            } else {
                printf("Transaction correctly marked as committed\n");
            }
        }
    }
    
    // Test transaction abort
    transaction_t* txn2 = txn_begin(tm, ISOLATION_REPEATABLE_READ);
    if (!txn2) {
        printf("Failed to begin second transaction\n");
        all_passed = false;
    } else {
        printf("Transaction %" PRIu64 " started for abort test\n", txn2->xid);
        
        if (txn_abort(tm, txn2) != 0) {
            printf("Failed to abort transaction\n");
            all_passed = false;
        } else {
            printf("Transaction aborted successfully\n");
            
            // Verify transaction is no longer active
            if (txn_is_active(tm, txn2->xid)) {
                printf("Transaction still active after abort\n");
                all_passed = false;
            } else {
                printf("Transaction correctly removed from active list after abort\n");
            }
        }
    }
    
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Test multiple concurrent transactions
static bool test_concurrent_transactions(void) {
    print_test_header("Testing Concurrent Transactions");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    bool all_passed = true;
    const int num_txns = 5;
    transaction_t* txns[num_txns];
    
    // Start multiple transactions
    printf("Starting %d concurrent transactions:\n", num_txns);
    for (int i = 0; i < num_txns; i++) {
        txns[i] = txn_begin(tm, ISOLATION_READ_COMMITTED);
        if (!txns[i]) {
            printf("Failed to start transaction %d\n", i);
            all_passed = false;
            break;
        }
        printf("  Transaction %d: XID %" PRIu64 "\n", i, txns[i]->xid);
    }
    
    // Verify all are active
    printf("Verifying all transactions are active:\n");
    for (int i = 0; i < num_txns; i++) {
        if (txns[i] && !txn_is_active(tm, txns[i]->xid)) {
            printf("Transaction %d (XID %" PRIu64 ") not active\n", i, txns[i]->xid);
            all_passed = false;
        }
    }
    
    // Print statistics
    printf("Transaction manager state:\n");
    txn_manager_print_stats(tm);
    
    // Commit half, abort half
    printf("Committing and aborting transactions:\n");
    for (int i = 0; i < num_txns; i++) {
        if (!txns[i]) continue;
        
        if (i % 2 == 0) {
            if (txn_commit(tm, txns[i]) == 0) {
                printf("  Committed transaction %d\n", i);
            } else {
                printf("  Failed to commit transaction %d\n", i);
                all_passed = false;
            }
        } else {
            if (txn_abort(tm, txns[i]) == 0) {
                printf("  Aborted transaction %d\n", i);
            } else {
                printf("  Failed to abort transaction %d\n", i);
                all_passed = false;
            }
        }
    }
    
    // Final statistics
    printf("Final transaction manager statistics:\n");
    txn_manager_print_stats(tm);
    
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Test MVCC snapshot functionality
static bool test_mvcc_snapshots(void) {
    print_test_header("Testing MVCC Snapshots");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    bool all_passed = true;
    
    // Create a transaction and get its snapshot
    transaction_t* txn = txn_begin(tm, ISOLATION_REPEATABLE_READ);
    if (!txn) {
        printf("Failed to begin transaction\n");
        txn_manager_destroy(tm);
        return false;
    }
    
    txn_snapshot_t* snapshot = txn->snapshot;
    if (!snapshot) {
        printf("Transaction has no snapshot\n");
        all_passed = false;
    } else {
        printf("Transaction %" PRIu64 " snapshot:\n", txn->xid);
        printf("  My XID: %" PRIu64 "\n", snapshot->my_xid);
        printf("  XMIN: %" PRIu64 "\n", snapshot->xmin);
        printf("  XMAX: %" PRIu64 "\n", snapshot->xmax);
        printf("  Active transactions: %u\n", snapshot->active_count);
        printf("  Isolation level: %s\n", isolation_level_to_string(snapshot->isolation_level));
        printf("  Snapshot time: %" PRIu64 "\n", snapshot->snapshot_time);
        
        if (snapshot->my_xid != txn->xid) {
            printf("Snapshot XID doesn't match transaction XID\n");
            all_passed = false;
        }
        
        if (snapshot->isolation_level != txn->isolation_level) {
            printf("Snapshot isolation level doesn't match transaction\n");
            all_passed = false;
        }
    }
    
    // Test snapshot copying
    txn_snapshot_t* copy = snapshot_copy(snapshot);
    if (!copy) {
        printf("Failed to copy snapshot\n");
        all_passed = false;
    } else {
        printf("Successfully copied snapshot\n");
        
        if (copy->my_xid != snapshot->my_xid ||
            copy->xmin != snapshot->xmin ||
            copy->xmax != snapshot->xmax ||
            copy->active_count != snapshot->active_count) {
            printf("Snapshot copy doesn't match original\n");
            all_passed = false;
        } else {
            printf("Snapshot copy matches original\n");
        }
        
        snapshot_destroy(copy);
    }
    
    txn_commit(tm, txn);
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Test MVCC visibility
static bool test_mvcc_visibility(void) {
    print_test_header("Testing MVCC Visibility");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    bool all_passed = true;
    
    // Create transactions
    transaction_t* txn1 = txn_begin(tm, ISOLATION_READ_COMMITTED);
    transaction_t* txn2 = txn_begin(tm, ISOLATION_READ_COMMITTED);
    
    if (!txn1 || !txn2) {
        printf("Failed to create transactions\n");
        if (txn1) txn_abort(tm, txn1);
        if (txn2) txn_abort(tm, txn2);
        txn_manager_destroy(tm);
        return false;
    }
    
    printf("Created transactions: %" PRIu64 " and %" PRIu64 "\n", txn1->xid, txn2->xid);
    
    // Create version info for testing
    version_info_t* version1 = version_create(txn1->xid, 1);
    version_info_t* version2 = version_create(txn2->xid, 1);
    
    if (!version1 || !version2) {
        printf("Failed to create version info\n");
        all_passed = false;
    } else {
        printf("Created version info for both transactions\n");
        
        // Test visibility from txn1's perspective
        printf("Testing visibility from transaction %" PRIu64 ":\n", txn1->xid);
        
        // txn1 should see its own version
        if (txn_tuple_visible(txn1->snapshot, version1)) {
            printf("  Transaction sees its own version: PASS\n");
        } else {
            printf("  Transaction doesn't see its own version: FAIL\n");
            all_passed = false;
        }
        
        // txn1 should not see uncommitted version from txn2
        if (!txn_tuple_visible(txn1->snapshot, version2)) {
            printf("  Transaction doesn't see other's uncommitted version: PASS\n");
        } else {
            printf("  Transaction sees other's uncommitted version: FAIL\n");
            all_passed = false;
        }
        
        // Mark version2 as committed
        version2->xmin_committed = true;
        
        // Test again - behavior depends on when txn1's snapshot was taken
        bool sees_committed = txn_tuple_visible(txn1->snapshot, version2);
        printf("  Transaction sees other's committed version: %s\n", 
               sees_committed ? "YES" : "NO");
        
        version_destroy(version1);
        version_destroy(version2);
    }
    
    txn_commit(tm, txn1);
    txn_commit(tm, txn2);
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Test lock management
static bool test_lock_management(void) {
    print_test_header("Testing Lock Management");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    bool all_passed = true;
    
    transaction_t* txn = txn_begin(tm, ISOLATION_READ_COMMITTED);
    if (!txn) {
        printf("Failed to begin transaction\n");
        txn_manager_destroy(tm);
        return false;
    }
    
    printf("Testing lock operations for transaction %" PRIu64 "\n", txn->xid);
    
    // Test acquiring locks
    printf("Acquiring table lock (shared):\n");
    if (txn_acquire_lock(tm, txn, LOCK_SHARED, LOCK_TABLE, 1, 0, 0) == 0) {
        printf("  Successfully acquired table lock\n");
        printf("  Locks held: %u\n", txn->held_lock_count);
    } else {
        printf("  Failed to acquire table lock\n");
        all_passed = false;
    }
    
    printf("Acquiring tuple lock (exclusive):\n");
    if (txn_acquire_lock(tm, txn, LOCK_EXCLUSIVE, LOCK_TUPLE, 1, 1, 100) == 0) {
        printf("  Successfully acquired tuple lock\n");
        printf("  Locks held: %u\n", txn->held_lock_count);
    } else {
        printf("  Failed to acquire tuple lock\n");
        all_passed = false;
    }
    
    // Print lock table
    printf("Current lock table state:\n");
    txn_manager_print_lock_table(tm);
    
    // Test lock compatibility
    printf("Testing lock compatibility:\n");
    printf("  SHARED + SHARED: %s\n", locks_compatible(LOCK_SHARED, LOCK_SHARED) ? "compatible" : "incompatible");
    printf("  SHARED + EXCLUSIVE: %s\n", locks_compatible(LOCK_SHARED, LOCK_EXCLUSIVE) ? "compatible" : "incompatible");
    printf("  EXCLUSIVE + EXCLUSIVE: %s\n", locks_compatible(LOCK_EXCLUSIVE, LOCK_EXCLUSIVE) ? "compatible" : "incompatible");
    
    // Test releasing all locks
    printf("Releasing all locks:\n");
    txn_release_all_locks(tm, txn);
    printf("  Locks held after release: %u\n", txn->held_lock_count);
    
    txn_commit(tm, txn);
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Test isolation levels
static bool test_isolation_levels(void) {
    print_test_header("Testing Isolation Levels");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    bool all_passed = true;
    
    // Test different isolation levels
    isolation_level_t levels[] = {
        ISOLATION_READ_UNCOMMITTED,
        ISOLATION_READ_COMMITTED,
        ISOLATION_REPEATABLE_READ,
        ISOLATION_SERIALIZABLE
    };
    
    const char* level_names[] = {
        "READ_UNCOMMITTED",
        "READ_COMMITTED", 
        "REPEATABLE_READ",
        "SERIALIZABLE"
    };
    
    for (int i = 0; i < 4; i++) {
        printf("Testing %s isolation level:\n", level_names[i]);
        
        transaction_t* txn = txn_begin(tm, levels[i]);
        if (!txn) {
            printf("  Failed to begin transaction with %s\n", level_names[i]);
            all_passed = false;
            continue;
        }
        
        printf("  Transaction %" PRIu64 " started with %s\n", 
               txn->xid, isolation_level_to_string(txn->isolation_level));
        
        if (txn->isolation_level != levels[i]) {
            printf("  Isolation level mismatch\n");
            all_passed = false;
        }
        
        if (txn->snapshot->isolation_level != levels[i]) {
            printf("  Snapshot isolation level mismatch\n");
            all_passed = false;
        }
        
        txn_commit(tm, txn);
    }
    
    // Test changing default isolation level
    printf("Testing default isolation level change:\n");
    if (txn_manager_set_isolation_level(tm, ISOLATION_SERIALIZABLE) == 0) {
        printf("  Successfully changed default isolation to SERIALIZABLE\n");
        
        transaction_t* txn = txn_begin(tm, tm->default_isolation);
        if (txn && txn->isolation_level == ISOLATION_SERIALIZABLE) {
            printf("  New transaction uses new default isolation\n");
            txn_commit(tm, txn);
        } else {
            printf("  New transaction doesn't use new default isolation\n");
            all_passed = false;
            if (txn) txn_abort(tm, txn);
        }
    } else {
        printf("  Failed to change default isolation level\n");
        all_passed = false;
    }
    
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Test transaction statistics
static bool test_transaction_statistics(void) {
    print_test_header("Testing Transaction Statistics");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    transaction_t* txn = txn_begin(tm, ISOLATION_READ_COMMITTED);
    if (!txn) {
        printf("Failed to begin transaction\n");
        txn_manager_destroy(tm);
        return false;
    }
    
    // Update transaction statistics
    txn_update_stats(txn, 100, 50, 10, 5);
    txn_update_stats(txn, 200, 25, 15, 8);
    
    printf("Transaction statistics after updates:\n");
    txn_print_stats(txn);
    
    txn_commit(tm, txn);
    
    // Test manager statistics
    printf("Transaction manager statistics:\n");
    txn_manager_print_stats(tm);
    
    txn_manager_destroy(tm);
    
    return true;
}

// Test error conditions
static bool test_error_conditions(void) {
    print_test_header("Testing Error Conditions");
    
    bool all_passed = true;
    
    // Test NULL parameter handling
    printf("Testing NULL parameter handling:\n");
    
    if (txn_manager_create() == NULL) {
        printf("  Transaction manager creation should not fail\n");
        all_passed = false;
    }
    
    transaction_manager_t* tm = txn_manager_create();
    
    if (txn_begin(NULL, ISOLATION_READ_COMMITTED) != NULL) {
        printf("  txn_begin with NULL manager should fail\n");
        all_passed = false;
    } else {
        printf("  txn_begin correctly rejects NULL manager\n");
    }
    
    if (txn_commit(tm, NULL) == 0) {
        printf("  txn_commit with NULL transaction should fail\n");
        all_passed = false;
    } else {
        printf("  txn_commit correctly rejects NULL transaction\n");
    }
    
    if (txn_is_active(NULL, 1)) {
        printf("  txn_is_active with NULL manager should return false\n");
        all_passed = false;
    } else {
        printf("  txn_is_active correctly handles NULL manager\n");
    }
    
    if (txn_is_active(tm, INVALID_TXN_ID)) {
        printf("  txn_is_active with invalid XID should return false\n");
        all_passed = false;
    } else {
        printf("  txn_is_active correctly handles invalid XID\n");
    }
    
    // Test double commit/abort
    printf("Testing double operations:\n");
    transaction_t* txn = txn_begin(tm, ISOLATION_READ_COMMITTED);
    if (txn) {
        if (txn_commit(tm, txn) == 0) {
            printf("  First commit succeeded\n");
            
            // Note: txn is freed after commit, so we can't test double commit
            // This is actually correct behavior
            printf("  Double commit test skipped (transaction freed after commit)\n");
        } else {
            printf("  First commit failed\n");
            all_passed = false;
        }
    }
    
    txn_manager_destroy(tm);
    
    return all_passed;
}

// Thread test data
struct thread_test_data {
    transaction_manager_t* tm;
    int thread_id;
    int num_operations;
    bool* results;
};

// Thread function for concurrent testing
static void* thread_test_func(void* arg) {
    struct thread_test_data* data = (struct thread_test_data*)arg;
    bool success = true;
    
    for (int i = 0; i < data->num_operations; i++) {
        transaction_t* txn = txn_begin(data->tm, ISOLATION_READ_COMMITTED);
        if (!txn) {
            success = false;
            break;
        }
        
        // Simulate some work
        usleep(1000); // 1ms
        
        // Randomly commit or abort
        if (i % 3 == 0) {
            if (txn_abort(data->tm, txn) != 0) {
                success = false;
                break;
            }
        } else {
            if (txn_commit(data->tm, txn) != 0) {
                success = false;
                break;
            }
        }
    }
    
    data->results[data->thread_id] = success;
    return NULL;
}

// Test concurrent access
static bool test_concurrent_access(void) {
    print_test_header("Testing Concurrent Access");
    
    transaction_manager_t* tm = txn_manager_create();
    if (!tm) {
        printf("Failed to create transaction manager\n");
        return false;
    }
    
    const int num_threads = 4;
    const int operations_per_thread = 10;
    
    pthread_t threads[num_threads];
    struct thread_test_data thread_data[num_threads];
    bool results[num_threads];
    
    printf("Starting %d threads with %d operations each:\n", num_threads, operations_per_thread);
    
    // Start threads
    for (int i = 0; i < num_threads; i++) {
        thread_data[i].tm = tm;
        thread_data[i].thread_id = i;
        thread_data[i].num_operations = operations_per_thread;
        thread_data[i].results = results;
        results[i] = false;
        
        if (pthread_create(&threads[i], NULL, thread_test_func, &thread_data[i]) != 0) {
            printf("Failed to create thread %d\n", i);
            
            // Wait for already created threads
            for (int j = 0; j < i; j++) {
                pthread_join(threads[j], NULL);
            }
            
            txn_manager_destroy(tm);
            return false;
        }
    }
    
    // Wait for all threads
    for (int i = 0; i < num_threads; i++) {
        pthread_join(threads[i], NULL);
        printf("  Thread %d: %s\n", i, results[i] ? "SUCCESS" : "FAILED");
    }
    
    // Print final statistics
    printf("Final transaction manager statistics:\n");
    txn_manager_print_stats(tm);
    
    bool all_passed = true;
    for (int i = 0; i < num_threads; i++) {
        if (!results[i]) {
            all_passed = false;
        }
    }
    
    txn_manager_destroy(tm);
    
    return all_passed;
}

int main() {
    printf("Transaction Manager Test Suite\n");
    printf("==============================\n");
    
    int passed = 0;
    int total = 0;
    
    // Run all tests
    struct {
        const char* name;
        bool (*test_func)(void);
    } tests[] = {
        {"Transaction Manager Creation", test_transaction_manager_creation},
        {"Transaction Lifecycle", test_transaction_lifecycle},
        {"Concurrent Transactions", test_concurrent_transactions},
        {"MVCC Snapshots", test_mvcc_snapshots},
        {"MVCC Visibility", test_mvcc_visibility},
        {"Lock Management", test_lock_management},
        {"Isolation Levels", test_isolation_levels},
        {"Transaction Statistics", test_transaction_statistics},
        {"Error Conditions", test_error_conditions},
        {"Concurrent Access", test_concurrent_access},
        {NULL, NULL}
    };
    
    for (int i = 0; tests[i].name != NULL; i++) {
        total++;
        bool result = tests[i].test_func();
        if (result) {
            passed++;
        }
        print_test_result(tests[i].name, result);
    }
    
    printf("\n=== Test Summary ===\n");
    printf("Passed: %d/%d\n", passed, total);
    printf("Success rate: %.1f%%\n", (double)passed / total * 100.0);
    
    return (passed == total) ? 0 : 1;
}